#include "fatfs.h"
#include "decode.h"
#include "config.h"
#include "Programmer.h"

//#define DebugPrintfHex          //Debug输出Hex有效数据
//#define DebugPrintfSaveHex      //Debug输出保存情况
//#define DebugPrintfStorageInfo  //Debug输出保存的固件段的地址和长度
//#define DebugPrintfReadUDiskio  //Debug输出读取hex的长度和上一次剩余的长度
enum 
{
	HexRowLenPos  = 1,
	HexAddrH8Pos  = 3,
	HexAddrL8Pos  = 5,
	HexRowTypePos = 7,
	HexDataPos    = 9,
};

/*********************************************************************
-
-   函数名: uint8_t AsciiToHex(uint8_t *DataBuf)
-   功能  : 将DataBuf中的无符号十六进制字符串转为十进制数
-           如DataBuf="00 1A"，则返回0x1a
-   输入  : 需要转换的首字节地址
-   返回  : 转换后的数据
-
**********************************************************************/
uint8_t AsciiToHex(uint8_t *DataBuf)
{
    int value=0;
    uint8_t i;
    for(i=0;i<2;i++)
    {
        if(DataBuf[i]<='9'&&DataBuf[i]>='0')
        {
            value=value*16+(int)(DataBuf[i]-'0'); //'0'
        }
        else if(DataBuf[i]<='f'&&DataBuf[i]>='a')
        {
            value=value*16+(int)(DataBuf[i]-'a'+10);
        }
        else
        {
            value=value*16+(int)(DataBuf[i]-'A'+10);
        }
    }
    return value;//返回转换后的数值

}

/*********************************************************************
-
-   函数名: uint8_t SaveProgrammNum(uint16_t *FlashOkNumBuf,const TCHAR* path)
-   功能  : 保存累计烧录数量
-   输入  : *FlashOkNumBuf:源数据指针，* path：工程文件
-   返回  : 保存状态,0:保存成功
-
**********************************************************************/
uint8_t SaveProgrammNum(FlashType *ProjectFile)
{
    FIL  		Savefsrc;
    FRESULT     Saveres;
    uint32_t    Savebr;
    char SaveTemp[]={"CH0:      FlashOk"};
    
    //保存信息
    const char ProjectAddr[]={"0:/FW/BLOG.txt"};
    sprintf(SaveTemp,"CH0:%6d FlashOk",ProjectFile->FlashOk[0]);
    Saveres = f_open( &Savefsrc , ProjectAddr,FA_CREATE_ALWAYS | FA_WRITE);
    f_lseek(&Savefsrc,0);
    if ( Saveres == FR_OK )
    {
        Saveres = f_write(&Savefsrc, SaveTemp, sizeof(SaveTemp), &Savebr);
    }
    f_close(&Savefsrc); 
    
    //保存成功数
    //先打开文件
    Saveres = f_open(&Savefsrc ,"0:/FW/info.bin",FA_CREATE_ALWAYS | FA_WRITE);
    if ( Saveres == FR_OK )
    {
        f_lseek(&Savefsrc,0);           //设置写入点
        //获取当前成功数
        SaveTemp[1] = ProjectFile->FlashOk[0] & 0xff;
        SaveTemp[0] = (ProjectFile->FlashOk[0]>>8) & 0xff;
        //写入数据
        Saveres = f_write(&Savefsrc, SaveTemp, 2, &Savebr);
    }
    f_close(&Savefsrc); 
    return Saveres;
}

/*********************************************************************
-
-   函数名: FRESULT SaveHextoProjectFile(uint32_t FlashStartAddr,
            uint32_t FlashDataLen,uint8_t *HexBuf,const TCHAR* path)
-   功能  : 将解析后的HEX文件内容和偏移地址写入工程文件、
-           格式：4个字节偏移地址+4个字节数据长度+24字节保留+1024字节Hex内容
-   输入  : FlashStartAddr：HEX内容偏移地址，4个字节，高位在前
-           FlashDataLen：数据长度，4个字节，高位在前
-           *HexBuf：Hex文件地址，1024字节
-           * path：工程文件绝对地址
-   返回  : 文件操作结果
-
**********************************************************************/
FRESULT SaveHextoProjectFile(uint32_t FlashStartAddr,uint32_t FlashDataLen,uint8_t *HexBuf,const TCHAR* path)
{
	static FIL  Savefsrc;
	FRESULT     Saveres;
    UINT        Savebr;
	uint8_t FlashMessage[StorageFwStart];		//4个字节偏移地址+4个字节数据长度+8字节保留
	
	FlashMessage[0] = FlashStartAddr>>24 & 0xff;
	FlashMessage[1] = FlashStartAddr>>16 & 0xff;
	FlashMessage[2] = FlashStartAddr>>8 & 0xff;
	FlashMessage[3] = FlashStartAddr>>0 & 0xff;
	
	FlashMessage[4] = FlashDataLen>>24 & 0xff;
	FlashMessage[5] = FlashDataLen>>16 & 0xff;
	FlashMessage[6] = FlashDataLen>>8 & 0xff;
	FlashMessage[7] = FlashDataLen>>0 & 0xff;
	memset(&FlashMessage[8],0xff,StorageFwStart-8);//默认为0xff
	Saveres = f_open( &Savefsrc , path, FA_WRITE);
	if ( Saveres == FR_OK )
	{
#ifdef DebugPrintf
#ifdef DebugPrintfSaveHex
		printf("f_open project OK \n\n");
#endif
#endif
		Saveres = f_lseek(&Savefsrc,f_size(&Savefsrc));
		if ( Saveres == FR_OK )
		{
			Saveres = f_write(&Savefsrc, FlashMessage, sizeof(FlashMessage), &Savebr);
			if ( Saveres == FR_OK )
			{
#ifdef DebugPrintf
#ifdef DebugPrintfSaveHex
				printf("f_write FlashMessage OK \n\n");
#endif
#endif
			}
			Saveres = f_write(&Savefsrc, HexBuf, 1024, &Savebr);
#ifdef DebugPrintf
#ifdef DebugPrintfStorageInfo
            printf("------- FlashAddr %.8x FlashLen = %d  ------\n",FlashStartAddr,FlashDataLen);
#endif
#ifdef DebugPrintfHex
            for(int ii=0;ii<1024;ii++)
            {
                if(ii%16 == 0)
                    printf("\n");
                printf(" %.2x ",HexBuf[ii]);	
            }
            printf("\n");
            printf("\n");
#endif
#endif
			if ( Saveres == FR_OK )
			{
#ifdef DebugPrintf
#ifdef DebugPrintfSaveHex
				printf("f_write FlashData OK \n\n");
#endif
#endif
			}
		}
	}
	f_close(&Savefsrc);
	return Saveres;
}
/*********************************************************************
-
-   函数名: uint8_t SaveProjectFile(FlashType *ProjectFile)
-   功能  : 保存工程文件在SD卡
-   输入  : *ProjectFile:烧录器工程项目结构体指针
-   返回  : 保存状态,0:保存失败,1:保存成功
-
**********************************************************************/
FRESULT     Saveres;
uint8_t SaveProjectFile(FlashType *ProjectFile)
{
    const char ProjectAddr[]={"0:/FW/release.bin"};
    const char HexAddr[]={"0:/FW/release.hex"};
    
    uint32_t CurrHex_i=0;   //循环变量，每一次读取256字节，然后解析出来
    uint8_t StartAddrFlag=0;//开始地址标志位，遇到新的保存地址，会标志为1,如0x400，0x800，如0x10000等，
	uint32_t sizelen;		//每次读取hex文件的长度
	uint8_t RowType=0;		//当前行记录的类型
	uint8_t ActiveLen;		//每一行Hex文件的有效长度,在每一行的第1-2字节
    uint8_t LastLen=0;      //每次读取256字节解析后不后一行的数据，会叠加在下一次读数的前面
	static uint8_t ReadData[310];	//每次读取的hex数据的保存缓冲区，需要> 读取字节数。
    uint32_t ReadHexLen=0;          //读取指针，用于记录上一次读取的地点
	uint32_t NewRowFlashAddr=0;     //新解析行的偏移地址
    uint32_t LastRowFlashAddr=0;    //上一次解析行的偏移地址
	uint32_t BasisFlashAddr=0;		//基础地址
	uint32_t FlashAddr=0;		    //要烧录的偏移地址
	static uint8_t FlashData[1024]; //要烧录到目标芯片的flash数据,这些数据都是去除地址的,起始地址是FlashAddr
	uint16_t FlashLen=0;	        //待烧录的长度,一般1024字节烧录一次,或者遇到跨度大的地址会烧录一次
    
	Saveres = f_unlink(ProjectAddr);
    Saveres = f_open( &USERFile ,ProjectAddr, FA_CREATE_NEW  | FA_WRITE);
    f_lseek(&USERFile,0);
    if ( Saveres == FR_OK )
    {  
        f_close(&USERFile);//关闭工程文件
		memset(FlashData,0xff,1024);//默认为0xff
		while(1)
		{
			Saveres = f_open( &USERFile , HexAddr, FA_READ);//打开HEX文件
			if ( Saveres == FR_OK )
			{
				f_lseek(&USERFile,ReadHexLen);                      //设置读取指针指向固件地址
				f_read(&USERFile,&ReadData[LastLen],256,&sizelen);  //读取HEX文件的256字节。
#ifdef DebugPrintf
                if(sizelen != 0)
                {
                    printf("Decode progress = %.2f%c\n",100.0*USERFile.fptr/USERFile.fsize,'%');
                }
#ifdef DebugPrintfReadUDiskio
				printf("sizelen = %d LastLen = %d\n",sizelen,LastLen);
#endif
#endif
				ReadHexLen = USERFile.fptr;							//记录本次读取的位置，这也是下一次要读取的开始位置
				f_close(&USERFile);									//读取完毕关闭文件
                
				sizelen += LastLen;     							//加上上一次解析的残余数据
				LastLen = 0;										//把上一次数据转移后头部后，LastLen清零
                
                //开始解析Hex数据
				for(CurrHex_i=0;CurrHex_i<sizelen;CurrHex_i++)  	//解析上一次解析的残余数据+本次读取的数据
				{
					if(ReadData[CurrHex_i] == 0x3a)					//检测Hex文件的行头
					{
						ActiveLen = AsciiToHex(&ReadData[CurrHex_i+HexRowLenPos]);//获取本行有效长度
						if((sizelen-CurrHex_i) >= (11+ActiveLen*2))//检测剩余未解析的数据是否大于或者等于本行的整帧长度
						{
							RowType = AsciiToHex(&ReadData[CurrHex_i+HexRowTypePos]);//获取本行类型
							//处理行类型
							switch(RowType)
							{
								case 0: //flash数据行
										//获取本行地址
									NewRowFlashAddr = (AsciiToHex(&ReadData[CurrHex_i+HexAddrH8Pos]) << 8) | AsciiToHex(&ReadData[CurrHex_i+HexAddrL8Pos]);
                                    if(StartAddrFlag == 0)
                                    {
                                        FlashAddr = NewRowFlashAddr;//记录为烧录偏移地址
                                        StartAddrFlag = 1;
                                    }
                                    //新的行地址比上一行的地址多48字节，说明跨扇区存储了，多见包含BootLoader的hex文件
                                    //需要把之前解析的数据先存储起来
									if((NewRowFlashAddr - LastRowFlashAddr>48) && FlashLen != 0)
									{
                                        FlashAddr = (FlashAddr/1024)*1024;//或者整K地址。有的FlashAddr不是规则是1024整数倍，这里转为1024整数倍
										SaveHextoProjectFile(BasisFlashAddr | FlashAddr,FlashLen,FlashData,ProjectAddr);//将转换后的HEX数据写进工程文件
										FlashLen = 0;   //标志需要烧录的数据长度为0
										FlashAddr = NewRowFlashAddr;//记录新的烧录偏移地址
										memset(FlashData,0xff,1024);//默认为0xff
									}
                                    //开始解析这一行数据
									for(uint8_t k=0;k<ActiveLen;k++)
									{
                                        //如果数据够1024字节，则开始存储一次
										if(FlashLen == 1024)
										{
                                            FlashAddr = (FlashAddr/1024)*1024;
											SaveHextoProjectFile(BasisFlashAddr | FlashAddr,FlashLen,FlashData,ProjectAddr);//将转换后的HEX数据写进工程文件
											FlashLen = 0;   //标志需要烧录的数据长度为0
											memset(FlashData,0xff,1024);//默认为0xff
											FlashAddr = NewRowFlashAddr + k;//记录新的烧录地址
										}
										//获取本行长度
										FlashData[(NewRowFlashAddr + k)%1024] = AsciiToHex(&ReadData[CurrHex_i+HexDataPos + k * 2]);
										FlashLen = (NewRowFlashAddr + k)%1024 +1;//更新需要烧录的长度
									}
									CurrHex_i += (ActiveLen*2);//更新解析的长度
									LastRowFlashAddr = NewRowFlashAddr + ActiveLen;//把这次的最后一个字节的地址记录为最后一行地址
								break;
								case 1:;break;// 01 HEX文件结束记录
								case 2:;break;// 02 扩展段地址记录
								case 3:;break;// 03 开始段地址记录
								case 4:		  // 04 扩展线性地址记录，用于扩展地址
                                    //遇到扩展行，需要把之前解析的数据先存储起来
                                    if(FlashLen != 0)
                                    {
                                        FlashAddr = (FlashAddr/1024)*1024;
                                        SaveHextoProjectFile(BasisFlashAddr | FlashAddr,FlashLen,FlashData,ProjectAddr);//将转换后的HEX数据写进工程文件
                                        FlashLen = 0;   //标志需要烧录的数据长度为0
                                        memset(FlashData,0xff,1024);//默认为0xff
                                        StartAddrFlag = 0;//记录新的烧录偏移地址
                                        FlashAddr = 0;
                                    }
									//获取基地址
									BasisFlashAddr = (AsciiToHex(&ReadData[CurrHex_i+HexDataPos]) << 8 | AsciiToHex(&ReadData[CurrHex_i + HexDataPos+2]))<<16 & 0xffff0000;
									LastRowFlashAddr = 0;
								break;
								case 5:;break;// 05 开始线性地址记录
							}
						}
						else
						{
							//没有有效的数据帧长度，把余下的数据复制到数组最前面
							for(LastLen = 0;CurrHex_i<sizelen;CurrHex_i++)
							{
								ReadData[LastLen++] = ReadData[CurrHex_i];
							}
						}
					}
				}
				if((sizelen-LastLen) == 0 && RowType == 0x01)//检测是否为结束行
				{
					FlashAddr = (FlashAddr/1024)*1024;
					SaveHextoProjectFile(BasisFlashAddr | FlashAddr,FlashLen,FlashData,ProjectAddr);//将转换后的HEX数据写进工程文件
					FlashLen = 0;
					FlashAddr = NewRowFlashAddr;
					memset(FlashData,0xff,1024);
					break;
				}
			}
		}
        return 1;//保存成功
    }
    else if ( Saveres == FR_EXIST )
    {
		f_close(&USERFile);
        return 2;//文件已存在,如果想新建,需要删除
    }
    else
    {
		f_close(&USERFile);
        return 0;//保存失败
    }
}
